package com.neborosoft.lib.collections.map;

import com.neborosoft.lib.Maps;

import java.util.*;

/**
 * Project: FlyingDog
 * User: stikhonenko
 * Date: 3/5/13
 * Time: 8:00 PM
 */
public class ArrayMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V> {
    private static final int PUT_ALL_COLLECTION_SIZE_WHEN_USE_PUT = 2;

    private List<ArrayMapEntry<K,V>> elements;
    private Comparator<K> comparator;

    private class ArrayMapEntry<K,V> implements Entry<K,V>, Comparable<K>{
        K key;
        V value;

        @Override
        public K getKey() {
            return null;
        }

        @Override
        public V getValue() {
            return null;
        }

        @Override
        public V setValue(V v) {
            return null;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            ArrayMapEntry that = (ArrayMapEntry) o;

            if (!key.equals(that.key)) return false;

            return true;
        }

        @Override
        public int hashCode() {
            return key.hashCode();
        }

        @Override
        public int compareTo(K key) {
            if(comparator == null){
                return ((Comparable)this.key).compareTo(key);
            }

            return ((Comparator)comparator).compare(this.key, key);
        }
    }

    public ArrayMap(Comparator<K> comparator){
        this.comparator = comparator;
        elements = new ArrayList<ArrayMapEntry<K, V>>();
    }

    public ArrayMap(Map<K,V> map, Comparator<K> comparator){
        this.comparator = comparator;
        putAll(map);
    }

    private ArrayMap(Object[][] array, Comparator<K> comparator){
        this(Maps.arrayToDuplicatedKeysMap(array), comparator);
    }

    public static <K,V> ArrayMap<K,V> fromArray(Object[][] array, Comparator<K> comparator){
        return new ArrayMap<K, V>(array, comparator);
    }

    public static <K,V> ArrayMap<K,V> fromArray(Object[][] array){
        return new ArrayMap<K, V>(array, null);
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        return new AbstractSet<Entry<K, V>>() {
            @Override
            public Iterator iterator() {
                return elements.iterator();
            }

            @Override
            public int size() {
                return elements.size();
            }
        };
    }

    @Override
    public Entry<K, V> lowerEntry(K k) {
        int index = Collections.binarySearch(elements, k);
        if(index < 0){
            index = -index - 1;
        }

        if(index < 0){
            return null;
        }

        return elements.get(index);
    }

    @Override
    public K lowerKey(K k) {
        return lowerEntry(k).getKey();
    }

    @Override
    public Entry<K, V> floorEntry(K k) {
        int index = Collections.binarySearch(elements, k);
        if(index >= 0){
            return elements.get(index);
        }

        return elements.get(-index - 1);
    }

    @Override
    public K floorKey(K k) {
        return floorEntry(k).getKey();
    }

    @Override
    public Entry<K, V> ceilingEntry(K k) {
        int index = Collections.binarySearch(elements, k);
        if(index >= 0){
            return elements.get(index);
        }

        return elements.get(-index);
    }

    @Override
    public K ceilingKey(K k) {
        return ceilingEntry(k).getKey();
    }

    @Override
    public Entry<K, V> higherEntry(K k) {
        int index = Collections.binarySearch(elements, k);
        if(index < 0){
            index = -index;
        }

        if(index >= elements.size()){
            return null;
        }

        return elements.get(index);
    }

    @Override
    public K higherKey(K k) {
        return higherEntry(k).getKey();
    }

    @Override
    public Entry<K, V> firstEntry() {
        return elements.get(0);
    }

    @Override
    public Entry<K, V> lastEntry() {
        return elements.get(elements.size() - 1);
    }

    @Override
    public Entry<K, V> pollFirstEntry() {
        return elements.remove(0);
    }

    @Override
    public Entry<K, V> pollLastEntry() {
        return elements.remove(elements.size() - 1);
    }

    @Override
    public NavigableMap<K, V> descendingMap() {
        throw new UnsupportedOperationException();
    }

    @Override
    public NavigableSet<K> navigableKeySet() {
        throw new UnsupportedOperationException();
    }

    @Override
    public NavigableSet<K> descendingKeySet() {
        throw new UnsupportedOperationException();
    }

    @Override
    public NavigableMap<K, V> subMap(K k, boolean b, K k2, boolean b2) {
        throw new UnsupportedOperationException();
    }

    @Override
    public NavigableMap<K, V> headMap(K k, boolean b) {
        throw new UnsupportedOperationException();
    }

    @Override
    public NavigableMap<K, V> tailMap(K k, boolean b) {
        throw new UnsupportedOperationException();
    }

    @Override
    public SortedMap<K, V> subMap(K k, K k2) {
        throw new UnsupportedOperationException();
    }

    @Override
    public SortedMap<K, V> headMap(K k) {
        throw new UnsupportedOperationException();
    }

    @Override
    public SortedMap<K, V> tailMap(K k) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Comparator<? super K> comparator() {
        return comparator;
    }

    @Override
    public K firstKey() {
        return firstEntry().getKey();
    }

    @Override
    public K lastKey() {
        return lastEntry().getKey();
    }

    @Override
    public V get(Object key) {
        int index = Collections.binarySearch(elements, (K)key);
        if(index < 0){
            return null;
        }

        return elements.get(index).getValue();
    }

    @Override
    public V put(K key, V value) {
        int index = Collections.binarySearch(elements, (K)key);
        V oldValue = null;
        if(index >= 0){
            ArrayMapEntry<K,V> entry = elements.get(index);
            oldValue = entry.value;
            entry.value = value;
        } else {
            ArrayMapEntry<K,V> entry = new ArrayMapEntry<K, V>();
            entry.key = key;
            entry.value = value;
            elements.add(-index - 1, entry);
        }

        return oldValue;
    }

    @Override
    public void putAll(Map<? extends K, ? extends V> map) {
        TreeMap<K,V> temp;
        if(comparator != null){
            temp = new TreeMap<K, V>(comparator);
        } else {
            temp = new TreeMap<K, V>();
        }

        temp.putAll(this);
        temp.putAll(map);

        Collection<Entry<K,V>> tempAsEntrySet = temp.entrySet();
        elements = new ArrayList<ArrayMapEntry<K, V>>(tempAsEntrySet.size());

        for(Entry<K,V> entry : tempAsEntrySet){
            ArrayMapEntry<K,V> arrayMapEntry = new ArrayMapEntry<K, V>();
            arrayMapEntry.key = entry.getKey();
            arrayMapEntry.value = entry.getValue();
            elements.add(arrayMapEntry);
        }
    }

    @Override
    public int size() {
        return elements.size();
    }
}
